/* Copyright (c) 2012 Stanford University
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/**
 * \file
 * Contains checksum utilities.
 */

#include <cinttypes>
#include <string>
#include <vector>

#ifndef LOGCABIN_CORE_CHECKSUM_H
#define LOGCABIN_CORE_CHECKSUM_H

namespace LogCabin {
namespace Core {
namespace Checksum {

/**
 * The number of bytes any checksum algorithm name and digest may occupy.
 */
enum { MAX_LENGTH = 256 };

/**
 * Returns the names of all available hash functions.
 * You can use any of these names in the calculate function.
 * \return
 *      The names in sorted order.
 */
std::vector<std::string>
listAlgorithms();

/**
 * Calculate the checksum for a chunk of data.
 * \param algorithm
 *      The checksum algorithm to use. Most crypto++ hash algorithms may be
 *      used, for example, "SHA-256". This function will PANIC if the algorithm
 *      is not available (see listAlgorithms()).
 * \param data
 *      The first byte of the data.
 * \param dataLength
 *      The number of bytes in the data.
 * \param[out] output
 *      The result of the hash function will be placed here.
 *      This will be a null-terminated, printable C-string.
 * \return
 *      The number of valid characters in 'output', including the null
 *      terminator. This is guaranteed to be greater than 1.
 */
uint32_t
calculate(const char* algorithm,
          const void* data, uint64_t dataLength,
          char output[MAX_LENGTH]);

/**
 * Calculate the checksum for a chunk of data.
 * \param algorithm
 *      The checksum algorithm to use. Most crypto++ hash algorithms may be
 *      used, for example, "SHA-256". This function will PANIC if the algorithm
 *      is not available (see listAlgorithms()).
 * \param data
 *      An list of (pointer, length) pairs describing what to checksum.
 * \param[out] output
 *      The result of the hash function will be placed here.
 *      This will be a null-terminated, printable C-string.
 * \return
 *      The number of valid characters in 'output', including the null
 *      terminator. This is guaranteed to be greater than 1.
 */
uint32_t
calculate(const char* algorithm,
          std::initializer_list<std::pair<const void*, uint64_t>> data,
          char output[MAX_LENGTH]);

/**
 * Check to see if the checksum format looks plausible.
 * \param checksum
 *      What might be a checksum as generated by calculate().
 * \param maxChecksumLength
 *      The total number of bytes in the buffer containing checksum, including
 *      the null terminator (if any).
 * \return
 *      If 'checksum' looks plausible and is safe to pass to verify(), returns
 *      the number of bytes in 'checksum' up to and including its null
 *      terminator. If 'checksum' looks malformed, this returns 0, which the
 *      caller should treat as a corrupt checksum.
 */
uint32_t
length(const char* checksum,
       uint32_t maxChecksumLength);

/**
 * Verify data against an existing checksum.
 * \param checksum
 *      A null-terminated checksum returned from calculate() or whose format
 *      has been verified by length(). The algorithm name in this checksum will
 *      determine which checksumming algorithm to use.
 * \param data
 *      The first byte of the data.
 * \param dataLength
 *      The number of bytes in the data.
 * \return
 *      An empty string if the checksum matches, or an error string if the
 *      checksum does not match or the checksum algorithm is not available.
 */
std::string
verify(const char* checksum,
       const void* data, uint64_t dataLength);

/**
 * Verify data against an existing checksum.
 * \param checksum
 *      A null-terminated checksum returned from calculate() or whose format
 *      has been verified by length(). The algorithm name in this checksum will
 *      determine which checksumming algorithm to use.
 * \param data
 *      An list of (pointer, length) pairs describing what to checksum.
 * \return
 *      An empty string if the checksum matches, or an error string if the
 *      checksum does not match or the checksum algorithm is not available.
 */
std::string
verify(const char* checksum,
       std::initializer_list<std::pair<const void*, uint64_t>> data);

} // namespace LogCabin::Core::Checksum
} // namespace LogCabin::Core
} // namespace LogCabin

#endif /* LOGCABIN_CORE_CHECKSUM_H */
